/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package sk.ke.szatmary.peter.jclac.simpleCiphers;

import sk.ke.szatmary.peter.jclac.simpleCiphers.core.SimpleCipherException;
import sk.ke.szatmary.peter.jclac.simpleCiphers.core.SimpleCipher;
import sk.ke.szatmary.peter.jclac.simpleCiphers.core.AbstractCipher;
import java.util.ArrayList;
import java.util.List;

/**
 * Vigener cipher
 * 
 * first attribute (0): String key<br />
 * 
 * @author nue
 */
public class Vigener extends AbstractCipher implements SimpleCipher {

    /**
     * one parametric constructor
     * @param attrs with atributes of cipher
     * @throws SimpleCipherException throw if is not fulfilled
     * <ul>
     * <li>one attribute</li>
     * <li>atribute with String type</li>
     * </ul>
     */
    public Vigener(List<SimpleCipherAttr> attrs) throws SimpleCipherException {
        this.attrs = attrs;
        this.checkAtributesForInit(attrs);


    }

    /**
     * constructor without parameters,
     * default atribute is Integer with value "key"
     */
    public Vigener() {
        attrs = new ArrayList<SimpleCipherAttr>();
        String keyStr = "key";
        attrs.add(new SimpleCipherAttr(keyStr));
    }

    /**
     * check initialization attributes on list
     * @param attrs attributes of cipher
     * @throws SimpleCipherException see one parametric constructor
     */
    private void checkAtributesForInit(List<SimpleCipherAttr> attrs) throws SimpleCipherException {
        // must be two atributes
        if (attrs.size() != 1) {
            throw new SimpleCipherException("Atributes in List<SimpleCipherAttr> must be 1, size of list must be exactly 1, found size " + attrs.size());
        }

        // cakame dve a urcitych typov inak exception
        for (int i = 0; i < attrs.size(); i++) {
            Object value = attrs.get(i).getValue();
            if (!((value instanceof String))) {
                throw new SimpleCipherException("Argument in List<SimpleCipherAttr> must be String");
            }
        }

        // key must be non empty string
        if (attrs.get(0).getValue().equals(EMPTY_STR)) {
            throw new SimpleCipherException("Key (atribute in List<SimpleCipherAttr>) must be non empty, must non like \"\"");
        }
    }

    /**
     * 
     * edit key value to right length, length of key must be the same like length of input text
     * @param key key for editing
     * @param input text string for crypting
     * @return tranformed key
     */
    private String transformAtribute(String key, String input) {
        StringBuilder str = new StringBuilder();
        if (key.length() != input.length()) {
            for (int i = 0; i < input.length(); i++) {
                str.append(key.charAt(i % key.length()));
            }
        }
        return str.toString();
    }

    @Override
    public String getCipherName() {
        return super.getName();
    }

    @Override
    public List<SimpleCipherAttr> getAttributes() {
        return super.getAtrributes();
    }

    @Override
    public void setAttributes(List<SimpleCipherAttr> list) throws SimpleCipherException {
        this.checkAtributesForInit(list);
        super.setAtrributeList(list);
        this.attrs.get(0).setChangedValue(null); // neviem ako sa ma zmenit, pokial nepoznam string
        // na desifrovanie alebo sifrovanie
    }

    @Override
    public String getEncrypted(String plain) {
        // ==============================================
        String key = (String) this.attrs.get(0).getValue();
        String keyTransformed = this.transformAtribute(key, plain);
        this.attrs.get(0).setChangedValue(keyTransformed);
        // ==============================================
   
        char[] encrypt = new char[plain.length()];
        for(int i=0;i<plain.length();i++) {
            
            int part = plain.charAt(i) + keyTransformed.charAt(i) -1;
            
            if(part<=END_UNICODE) {
                encrypt[i] = (char) part;
            } else {
                encrypt[i] = (char) (part % END_UNICODE);
            }
        }
        return new String(encrypt);
    }

    @Override
    public String getDecrypted(String encrypted) {
        // ==============================================
        String key = (String) this.attrs.get(0).getValue();
        String keyTransformed = this.transformAtribute(key, encrypted);
        this.attrs.get(0).setChangedValue(keyTransformed);
        // ==============================================
        
        String keyTranformed = (String) this.attrs.get(0).getChangedValue();
        
         char[] decrypt = new char[encrypted.length()];
         
        for(int i=0;i<encrypted.length();i++) {
            int part = encrypted.charAt(i) - keyTranformed.charAt(i)  + 1;
            
            if(part > 0) {
                decrypt[i] = (char) part;
            } else {
                decrypt[i] = (char) (part + END_UNICODE);
            }
        }
        return new String(decrypt);
    }

    @Override
    public String toString() {
        return this.getName() +"{" + "key=" +
                (String) this.attrs.get(0).getValue() + 
                "transformed_key="+ 
                (String) this.attrs.get(0).getChangedValue() + '}';
    }
    
    
    
}
